/* TaskList.cpp
Copyright (c) 2008 Michael Zahniser
Please note the license terms (MIT license) at the end of this document.

Function definitions for the TaskList class.
*/

#include "TaskList.h"



// Default constructor:
// Row -1 is considered to be the root of the tree.
TaskList::TaskList()
	: selection(-1, 0)
{
	push_back(Task());
	Update(begin(), begin());
}



// Copy constructor:
TaskList::TaskList(const TaskList &tasks)
{
	*this = tasks;
}



// Assignment operator:
TaskList &TaskList::operator=(const TaskList &tasks)
{
	// Copy the tasks, and make sure the rows array is up to date.
	list<Task>::operator=(tasks);
	Update(begin(), begin());
	selection = tasks.selection;

	return *this;
}



// Name:
// Get the name of this list.
const string &TaskList::Name() const
{
	// The list's name is the text of the root task.
	return front().Text();
}



// SetName:
// Set the name of this list.
void TaskList::SetName(const string &name)
{
	// The list's name is the text of the root task.
	front().SetText(name);
}



// SelectUp:
// Select the previous row. Loop around if we reach the beginning.
void TaskList::SelectUp()
{
	--selection;
	if(selection == -2)
		selection = Rows() - 1;
}



// SelectDown:
// Select the next row. Loop around if we are at the end.
void TaskList::SelectDown()
{
	++selection;
	if(selection == Rows())
		selection = -1;
}



// SelectSiblingUp:
// Select the previous task that is a sibling of this one.
void TaskList::SelectSiblingUp()
{
	SelectSibling(&TaskList::SelectUp);
}



// SelectSiblingDown:
// Select the next task that is a sibling of this one.
void TaskList::SelectSiblingDown()
{
	SelectSibling(&TaskList::SelectDown);
}



// SelectParent:
// Find the parent of this task, and select it.
void TaskList::SelectParent()
{
	int depth = (*this)[selection].Depth();
	if(!depth)
	{
		selection = -1;
		return;
	}

	do {
		SelectUp();
	} while((*this)[selection].Depth() >= depth);
}



// CursorUp:
// Move the cursor up (expanding the selection) without changing the position of the
// anchor point of the selection.
void TaskList::CursorUp()
{
	if(selection.Cursor() > 0)
		selection--;
}



// CursorDown:
// Move the cursor down (expanding the selection) without changing the position of the
// anchor point of the selection.
void TaskList::CursorDown()
{
	if((selection.Cursor() != -1) & (selection.Cursor() != Rows() - 1))
		selection++;
}



// SelectRoot:
// Select the root of the display (row index -1).
void TaskList::SelectRoot()
{
	selection = -1;
}



// SelectEnd:
// Select the last row of the display.
void TaskList::SelectEnd()
{
	selection = Rows() - 1;
}



// SelectChange:
// Add the given amount to the current selected row index, making sure not to move
// the selection outside the existing rows.
void TaskList::SelectChange(int delta)
{
	selection += delta;
	if(selection < -1)
		selection = -1;
	else if(selection >= Rows())
		selection = Rows() - 1;
}



// SelectRow:
// Set the selection to the given row index.
void TaskList::SelectRow(int row)
{
	selection = row;
}



// SetCursor:
// Expand the selection to include the given row index.
void TaskList::SetCursor(int row)
{
	selection.SetCursor(row);
}



// SelectAll:
// Expand the selection to include the entire tree.
void TaskList::SelectAll()
{
	selection.SetFirstAndLast(0, Rows() - 1);
}



// Rows:
// Get the number of visible rows.
int TaskList::Rows() const
{
	return static_cast<int>(rows.size()) - 1;
}



// Operator[]:
// Get the task at the given row index. If the index is out of bounds, return the root task.
const Task &TaskList::operator[](int row) const
{
	if((row < 0) | (row >= Rows()))
		return *begin();
	
	return *(rows[row + 1].it);
}



// Children:
// Get the number of children of the given row.
// The return value is 0 if this row is the end of the tree
// at its depth.
int TaskList::Children(int row) const
{
	return rows[row + 1].children;
}



// SelectSibling:
// Select a sibling in the given direction.
void TaskList::SelectSibling(void (TaskList::*move)(void))
{
	// The root task has no siblings.
	if(selection == -1)
		return;

	Selection originalSelection = selection;

	// Move in the given direction until we find something at or below
	// the currently selected node's depth.
	int depth = (*this)[selection].Depth();
	do {
		(this->*move)();
	} while((*this)[selection].Depth() > depth);

	// Don't change the selection if it turns out that the task we found
	// is not a sibling of the currently selected one.
	if((*this)[selection].Depth() < depth)
		selection = originalSelection;
}



// Update:
// Update the array of visible rows, and update the selection to contain
// everything in the range specified. For each visible row, determine how
// many visible children it has. This information is used for displaying.
void TaskList::Update(iterator first, iterator last)
{
	rows.clear();
	rows.push_back(Row(begin()));
	
	static vector<int> parents;
	parents.clear();
	parents.push_back(0);
	
	// Current depth = size of parents - 1.
	// Current row = size of rows.
	int firstRow = -1;
	int lastRow = -1;
	
	for(iterator it = begin(); it != end(); ++it)
	{
		// If we match one of the iterators, update the selection.
		if(it == first)
			firstRow = static_cast<int>(rows.size()) - (1 + !it->IsRevealed());
		else if(it == last)
			lastRow = static_cast<int>(rows.size()) - (1 + !it->IsRevealed());

		// Skip tasks that are not revealed.
		if(!it->IsRevealed())
			continue;
		
		// Update the child counts.
		while(static_cast<int>(parents.size()) - 1 > it->Depth())
		{
			int children = rows[parents.back()].children;
			// A children count of 0 means the end of a tree or subtree.
			rows[parents.back()].children = 0;
			parents.pop_back();
			rows[parents.back()].children += children;
		}
		if(static_cast<int>(parents.size()) - 1 == it->Depth())
		{
			int children = rows[parents.back()].children;
			parents.pop_back();
			rows[parents.back()].children += children;
		}

		// This row is, at least temporarily, a new "parent".
		parents.push_back(static_cast<int>(rows.size()));
		rows.push_back(Row(it));
	}

	// Update the child counts for everything remaining in the stack.
	// A children count of 0 means the end of a tree or subtree.
	while(!parents.empty())
	{
		rows[parents.back()].children = 0;
		parents.pop_back();
	}

	// If the "last" iterator is the end of the list, set lastRow to the last row.
	if(end() == last)
		lastRow = static_cast<int>(rows.size()) - 1;

	// Update the selection based on the matches we found to first and last.
	selection.SetFirstAndLast(firstRow, lastRow);
	if(selection.Last() > selection.First())
		selection.SetFirstAndLast(selection.First(), selection.Last() - 1);
}



/* Copyright (c) 2008 Michael Zahniser

Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
files (the "Software"), to deal in the Software without
restriction, including without limitation the rights to use,
copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following
conditions:

The above copyright notice and this permission notice shall be
included in all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE. */
